package generics;

import java.util.ArrayList;
import java.util.List;

/**
 * 我们在定义泛型类，泛型方法，泛型接口的时候经常会碰见很多不同的通配符，比如 T，E，K，V 等等，这些通配符又都是什么意思呢？
 * 常用的 T，E，K，V，？
 * 本质上这些个都是通配符，没啥区别，只不过是编码时的一种约定俗成的东西。
 * 比如上述代码中的 T ，我们可以换成 A-Z 之间的任何一个 字母都可以，并不会影响程序的正常运行，但是如果换成其他的字母代替 T ，在可读性上可能会弱一些。
 * 通常情况下，T，E，K，V，？是这样约定的：
 *
 * ？表示不确定的 java 类型
 * T (type) 表示具体的一个java类型
 * K V (key value) 分别代表java键值中的Key Value
 * E (element) 代表Element
 *
 * ？无界通配符
 *
 * 先从一个小例子看起，原文在 这里 。
 *
 * 我有一个父类 Animal 和几个子类，如狗、猫等，现在我需要一个动物的列表，我的第一个想法是像这样的：
 *
 * List<Animal> listAnimals
 *
 * 但是老板的想法确实这样的：
 *
 * List<? extends Animal> listAnimals
 *
 * 为什么要使用通配符而不是简单的泛型呢？
 * 通配符其实在声明局部变量时是没有什么意义的，但是当你为一个方法声明一个参数时，它是非常重要的。
 *
 * static int countLegs (List<? extends Animal > animals ) {
 *     int retVal = 0;
 *     for ( Animal animal : animals )
 *     {
 *         retVal += animal.countLegs();
 *     }
 *     return retVal;
 * }
 *
 * static int countLegs1 (List< Animal > animals ){
 *     int retVal = 0;
 *     for ( Animal animal : animals )
 *     {
 *         retVal += animal.countLegs();
 *     }
 *     return retVal;
 * }
 *
 * public static void main(String[] args) {
 *     List<Dog> dogs = new ArrayList<>();
 *   // 不会报错
 *     countLegs( dogs );
 *  // 报错
 *     countLegs1(dogs);
 * }
 *
 * 当调用 countLegs1 时，就会飘红，提示的错误信
 *
 *
 * 所以，对于不确定或者不关心实际要操作的类型，可以使用无限制通配符（尖括号里一个问号，即 <?> ），
 * 表示可以持有任何类型。像 countLegs 方法中，限定了上届，但是不关心具体类型是什么，
 * 所以对于传入的 Animal 的所有子类都可以支持，并且不会报错。而 countLegs1 就不行。
 * */
public class Lesson2 {

    public static void main(String[] args) {
        List<Dog> dogs = new ArrayList<>();

        // 报错
        // printAnimal1(dogs);

        // 不会报错
        printAnimal2(dogs);
    }

    static void printAnimal1(List<Animal> animals){
        animals.stream().forEach(System.out::println);
    }

    static void printAnimal2(List<? extends Animal> animals){
        animals.stream().forEach(System.out::println);
    }

    class Animal{

    }

    class Dog extends Animal{

    }
}
